import * as d3 from "d3";
// import * as DEFAULT from "@/utils/graph/default";
import * as Node from "@/utils/graph/Node";

export function ElementEditor(ctx) {

  let context = ctx;

  /**---------------------------------------------------------------------------
   * 添加一个节点
   * @param node
   * @param reshow 是否重启绘制流程
   * @param remember 是否记录这次操作
   */
  this.addNode = function(node,reshow=true,remember=true) {
    context.kgInfo.nodes.push(node);
    context.kgInfo.visibleNodes.push(node);

    if(remember) context.historyManager.startRemember();
    context.historyManager.rememberAddNode(node);
    if(remember) context.historyManager.endRemember();

    if(reshow){

      context.dataFormatter.formatNodes([node]);

      let nodes = context.kgInfo.visibleNodes;
      context.drawer.drawNode(nodes);
      context.drawer.drawNodeText(nodes);
      context.drawer.drawButtonGroup(nodes);

      context.eventManager.initNodeEvent();
      context.eventManager.initButtonEvent();

      context.simulation.restartNodesSimulation();

      this.refreshShow();

    }

    this.informNodeUpdate();

  };

  /**---------------------------------------------------------------------------
   * 添加一个连接
   * @param link
   * @param reshow
   * @param remember 是否开始记录
   */
  this.addLink = function(link,reshow=true,remember=true) {
    context.kgInfo.links.push(link);
    context.kgInfo.visibleLinks.push(link);

    if(remember) context.historyManager.startRemember();
    context.historyManager.rememberAddLink(link);
    if(remember) context.historyManager.endRemember();

    if(reshow){

      let links = context.kgInfo.visibleLinks;
      context.dataFormatter.formatLinks(links);

      context.drawer.drawLink(links);
      context.drawer.drawLinkText(links);

      context.eventManager.initLinkEvent();

      context.simulation.restartLinksSimulation(links);

      this.refreshShow();

    }

    this.informLinkUpdate();

  };

  /**---------------------------------------------------------------------------
   * 修改一个已存在节点
   * @param node
   * @param reshow
   * @param remember
   */
  this.editNode = function(node,reshow=true,remember=true) {
    this.giveUpNode();

    let curNode = this.getNodeById(node.uuid);
    let curNodeCopy = JSON.parse(JSON.stringify(curNode));

    curNode.uri = node.uri;
    curNode.label = node.label;

    if(curNode.type.id != node.id){
      context.typeManager.removeFromTypeList(curNode);
      context.typeManager.mapToTypeList(curNode,node.type);
    }

    // delete node.selfColor;
    // delete node.selfR;
    context.dataFormatter.modifyProperty(curNode,node,"color","selfColor");
    let isRChanged = context.dataFormatter.modifyProperty(curNode,node,"r","selfR");

    if(isRChanged && reshow){
      context.drawer.drawToolButton();
      let out_buttongroup_id = ".out_buttongroup_" + curNode.uuid;
      d3.select(out_buttongroup_id)
        .attr("r",curNode.r)
        .attr("xlink:href", "#out_circle_" + curNode.r) //  指定 use 引用的内容;
      context.eventManager.initButtonEvent();
    }

    if(remember) context.historyManager.startRemember();
    context.historyManager.rememberEditNode(curNodeCopy,curNode);
    if(remember) context.historyManager.endRemember();

    if(reshow){
      delete curNode.font_size;
      context.dataFormatter.formatNodes([curNode]);

      context.qaGraphNode
        .selectAll("circle[uuid='" + curNode.uuid + "']")
        .style("fill",curNode.color)
        .attr("r", curNode.r)
        .select("title")
        .text(curNode.label);

      context.qaGraphNodeText
        .selectAll("text[uuid='" + curNode.uuid + "']")
        .attr("font-size",function(){
          return Node.fontSize(curNode);
        })
        .attr("font-weight",Node.fontWeight)
        .attr("dy",Node.fontDy)
        .text(curNode.label)
        .select("title")
        .text(curNode.label)

      context.simulation.restartNodesSimulation();
      context.simulation.restartLinksSimulation();

      this.refreshShow();
    }

    this.informNodeUpdate();

  };

  /**---------------------------------------------------------------------------
   * 修改一个已存在连接
   * @param link
   * @param reshow
   * @param remember
   */
  this.editLink = function(link,reshow=true,remember=true) {

    let curLink = this.getLinkById(link.uuid);

    if(remember) context.historyManager.startRemember();
    context.historyManager.rememberEditLink(curLink,link);
    if(remember) context.historyManager.endRemember();

    this.deleteLinkById(link.uuid,false,false);
    this.addLink(link,reshow,false);

    this.informLinkUpdate();
  }

  /**---------------------------------------------------------------------------
   * 根据id删除节点
   * @param deleteId
   * @param reshow
   * @param remember
   */
  this.deleteNodeById = function(deleteId,reshow=true,remember=true) {
    let node = this.getNodeById(deleteId);

    //如果当前节点被选择则清空
    if(context.selectedNode && context.selectedNode.uuid == deleteId)
      context.selectedNode = null;

    context.typeManager.removeFromTypeList(node);

    if(remember) context.historyManager.startRemember();

    removeNodesItemById(deleteId,context.kgInfo.nodes,true);
    removeNodesItemById(deleteId,context.kgInfo.visibleNodes,false)

    let needDelLink = removeLinksItemByEndPoint(deleteId,context.kgInfo.links,true);
    removeLinksItemByEndPoint(deleteId,context.kgInfo.visibleLinks,false);

    if(remember) context.historyManager.endRemember();

    if(reshow){
      context.dataFormatter.formatData();
      let links = context.kgInfo.visibleLinks;
      let nodes = context.kgInfo.visibleNodes;

        context.drawer.drawLink(links);
        context.drawer.drawLinkText(links);

        context.eventManager.initLinkEvent();

      context.drawer.drawNode(nodes);
      context.drawer.drawNodeText(nodes);
      context.drawer.drawButtonGroup(nodes);

      context.eventManager.initNodeEvent();
      context.eventManager.initButtonEvent();

      context.simulation.restartNodesSimulation(nodes);
      context.simulation.restartLinksSimulation(links);

      this.refreshShow();
    }

    if(needDelLink) this.informLinkUpdate();
    this.informNodeUpdate();

  };

  /**---------------------------------------------------------------------------
   * 根据id删除连接
   * @param deleteId
   * @param reshow
   * @param remember 是否记录历史记录
   */
  this.deleteLinkById = function(deleteId,reshow=true,remember=true) {

    if(remember) context.historyManager.startRemember();
    removeLinksItemById(deleteId,context.kgInfo.links,true);
    removeLinksItemById(deleteId,context.kgInfo.visibleLinks,false);
    if(remember) context.historyManager.endRemember();

    if(reshow){
      context.dataFormatter.formatData();
      let links = context.kgInfo.visibleLinks;

      context.drawer.drawLink(links);
      context.drawer.drawLinkText(links);

      context.eventManager.initLinkEvent();

      context.simulation.restartLinksSimulation(links);

      this.refreshShow();
    }

    this.informLinkUpdate();

  };

  /**---------------------------------------------------------------------------
   * 根据id获取节点（直接指向kgInfo中nodes内的引用）
   * @param id
   * @returns {null|*}
   */
  this.getNodeById = function(id) {
    return getArrItemByID(id,context.kgInfo.nodes);
  }

  /**---------------------------------------------------------------------------
   * 根据id获取连接（直接指向kgInfo中links内的引用）
   * @param id
   * @returns {null|*}
   */
  this.getLinkById = function(id) {
    return getArrItemByID(id,context.kgInfo.links);
  }

  /**---------------------------------------------------------------------------
   * 根据类型获取节点集合
   * @param typeName
   */
  this.getNodesByTypeName = function(typeName) {
    let nodes = context.kgInfo.nodes;
    let result = [];
    nodes.forEach(function(node) {
      if(node.type.name == typeName) {
        result.push(node);
      }
    })
    return result;
  }

  /**---------------------------------------------------------------------------
   * 根据类型获取节点集合
   * @param typeName
   */
  this.getNodesByTypeId = function(typeId) {
    let nodes = context.kgInfo.nodes;
    let result = [];
    nodes.forEach(function(node) {
      if(node.type.id == typeId) {
        result.push(node);
      }
    })
    return result;
  }

  /**---------------------------------------------------------------------------
   * 选择当前节点
   * @param node
   */
  this.focusNode = function(node) {
    context.selectedNode = node;
    node.fx = node.x;
    node.fy = node.y;
    context.kgInfo.visibleNodes.forEach(function(n){
      context.buttonGroupManager.closeButtonGroup(n);
    });
    context.buttonGroupManager.showButtonGroup(node);
    context.selectedNodeUpdate = new Date().getTime();
  }
  this.giveUpNode = function() {
    if(context.selectedNode && context.selectedNode.uuid){
      context.buttonGroupManager.closeButtonGroup(context.selectedNode);
      context.selectedNode = null;
      context.selectedNodeUpdate = new Date().getTime();
    }
  }

  /**---------------------------------------------------------------------------
   * 展示节点相关节点与连接 隐藏其他（一定透明度）
   * @param d
   */
  this.showNodeRelated = function(d) {
    let _this = context;
    let links = _this.kgInfo.visibleLinks;

    //todo鼠标放上去只显示相关节点，其他节点和连线隐藏
    d3.selectAll(".node").style("fill-opacity", 0.1);
    let relvantNodeIds = [];
    var relvantNodes = links.filter(function(n) {
      return n.sId === d.uuid || n.tId === d.uuid;
    });
    relvantNodes.forEach(function(item) {
      relvantNodeIds.push(item.sId);
      relvantNodeIds.push(item.tId);
    });
    //显示相关的节点 以及被选中节点
    _this.qaGraphNode.selectAll("circle").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid === d.uuid ||
        context.selectedNode && c.uuid == context.selectedNode.uuid) {
        return 1.0;
      }
    });
    //透明所有节点文字
    d3.selectAll(".nodetext").style("fill-opacity", 0.1);
    //显示相关的节点文字
    _this.qaGraphNodeText.selectAll("text").style("fill-opacity", function(c) {
      if (relvantNodeIds.indexOf(c.uuid) > -1 || c.uuid === d.uuid ||
        context.selectedNode && c.uuid == context.selectedNode.uuid) {
        return 1.0;
      }
    });
    //透明所有连线、箭头
    d3.selectAll(".line")
      .style("stroke-opacity", 0.1)
      .selectAll("path")
      .attr("marker-end", "none");
    //显示相关的连线
    _this.qaGraphLink
      .selectAll(".link")
      .style("stroke-opacity", function(c) {
        if (c.tId === d.uuid || c.sId === d.uuid) {
          return 1.0;
        }
      })
      .attr("marker-end", function(c) {
        //展示相关箭头
        if (c.tId === d.uuid || c.sId === d.uuid) {
          return "url(#arrow)";
        }
      });
    //透明所有连线文字
    d3.selectAll(".linetext").style("fill-opacity", 0.1);
    //显示相关的连线文字
    _this.qaGraphLinkText
      .selectAll(".linktext")
      .style("fill-opacity", function(c) {
        if (c.tId === d.uuid || c.sId === d.uuid ||
          context.selectedNode && c.uuid == context.selectedNode.uuid) {
          return 1.0;
        }
      });
  }

  /**---------------------------------------------------------------------------
   * 显示所有图元
   */
  this.showAll = function() {
    d3.selectAll(".node").style("fill-opacity", 1);
    d3.selectAll(".nodetext").style("fill-opacity", 1);
    d3.selectAll(".link")
      .style("stroke-opacity", 1)
      .attr("marker-end", "url(#arrow)");
    d3.selectAll(".linktext").style("fill-opacity", 1);
  }

  /**---------------------------------------------------------------------------
   * 展示某种类型的节点
   * @param selectTypeIdArr
   */
  this.switchTypeShow = function(selectTypeIdArr) {
    let _this = this;
    let typeList = context.kgInfo.typeList;
    typeList.forEach(function(type) {
      if(selectTypeIdArr.includes(type.id)){
        type.hide = false;
        showTypeNode(_this.getNodesByTypeName(type.name));
      }
      else{
        type.hide = true;
        hideTypeNode(_this.getNodesByTypeName(type.name));
      }
    });
    adjustLinkShow();

    // context.dataFormatter.formatData();
    context.drawer.drawAll();
    context.eventManager.initDefaultEvent();
    context.simulation.restartNodesSimulation(false);
    context.simulation.restartLinksSimulation();
    this.refreshShow();

  }

  /**---------------------------------------------------------------------------
   * 刷新所有显示
   */
  this.refreshShow = function() {
    context.zoom.zoomIn();
    context.zoom.zoomOut();
    this.showAll();
  }

  /**---------------------------------------------------------------------------
   * 通知更新
   */
  this.informNodeUpdate = function() {
    if(context.update == undefined || context.update == null) context.update = {};
    context.update.nodes = new Date().getTime();
  }
  this.informLinkUpdate = function() {
    if(context.update == undefined || context.update == null) context.update = {};
    context.update.links = new Date().getTime();
  }

  /**---------------------------------------------------------------------------
   * 固定、释放所有节点
   */
  this.fixAll = function() {
    let nodes = context.kgInfo.nodes;
    for(let i = 0;i<nodes.length;i++){
      nodes[i].fx = nodes[i].x;
      nodes[i].fy = nodes[i].y;
    }
  }
  this.freeAll = function() {
    let nodes = context.kgInfo.nodes;
    // context.simulation.alphaTarget(0).alphaDecay(0.03);
    for(let i = 0;i<nodes.length;i++){
      nodes[i].fx = null;
      nodes[i].fy = null;
    }
  }

  /**---------------------------------------------------------------------------
   * 根据id获取对象数组的某一元素 对象必须含有id属性
   * @param id
   * @param arr
   * @returns {null|*}
   */
  function getArrItemByID(id, arr) {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].uuid == id) {
        return arr[i];
      }
    }
    return null;
  }

  /**---------------------------------------------------------------------------
   * 根据id从nodes数组中删除某一元素
   * @param deleteId
   * @param nodes
   * @param isInsertHistory
   */
  function removeNodesItemById(deleteId,nodes,isInsertHistory=false) {
    let deletedNode = removeArrItemById(deleteId,nodes);
    if(deletedNode==null) return false;
    if(isInsertHistory) context.historyManager.rememberDeleteNode(deletedNode);
    return true;
  }

  /**---------------------------------------------------------------------------
   * 根据id从links数组中删除某一元素
   * @param deleteId
   * @param links
   * @param isInsertHistory
   */
  function removeLinksItemById(deleteId,links,isInsertHistory=false) {
    let deletedLink = removeArrItemById(deleteId,links);
    if(deletedLink==null) return false;
    if(isInsertHistory) context.historyManager.rememberDeleteLink(deletedLink);
    return true;
  }

  /**---------------------------------------------------------------------------
   * 根据id删除对象数组的某一元素 对象必须含有id属性
   * @param deleteId
   * @param arr
   */
  function removeArrItemById(deleteId, arr) {
    for (let i = 0; i < arr.length; i++) {
      if (arr[i].uuid == deleteId) {
        let deletedElement = arr[i];
        arr.splice(i, 1);
        return deletedElement;
      }
    }
    return null;
  }

  /**---------------------------------------------------------------------------
   * 根据端点删除links数组所有连接
   * @param deleteId 要删除的node的id
   * @param links
   * @param ifInsertHistory 是否
   * @returns {boolean}
   */
  function removeLinksItemByEndPoint(deleteId, links, ifInsertHistory=false) {
    let needDelLink = false;
    for (let i = 0; i < links.length; i++) {
      if (
        links[i].sId == deleteId ||
        links[i].tId == deleteId
      ) {

        if(ifInsertHistory)context.historyManager.rememberDeleteLink(links[i]);

        links.splice(i--, 1); //删除后数组长度变短 需减1
        needDelLink = true;
      }
    }
    return needDelLink;
  }

  /**---------------------------------------------------------------------------
   * 展示某种类型的节点
   * @param curTypeNodes
   */
  function showTypeNode(curTypeNodes) {
    let visibleNodes = context.kgInfo.visibleNodes;
    curTypeNodes.forEach(function(node) {
      node.hide = false;
      if(!visibleNodes.includes(node)){
        visibleNodes.push(node);
      }
    });
  }

  /**---------------------------------------------------------------------------
   * 隐藏某种类型节点
   * @param curTypeNodes
   */
  function hideTypeNode(curTypeNodes) {
    let visibleNodes = context.kgInfo.visibleNodes;
    curTypeNodes.forEach(function(node) {
      node.hide = true;
      removeNodesItemById(node.uuid,visibleNodes);
    });
  }

  /**---------------------------------------------------------------------------
   * 根据连接两边节点隐藏情况判定连接是否需要隐藏并实施
   */
  function adjustLinkShow(){
    let links = context.kgInfo.links;
    let visibleLinks = context.kgInfo.visibleLinks;

    links.forEach(function(link) {
      let isInVisibleLink = visibleLinks.includes(link);
      if(!link.source.hide && !link.target.hide){
        if(!isInVisibleLink){
          link.hide = false;
          visibleLinks.push(link);
        }
      }
      else {
        if(isInVisibleLink){
          link.hide = true;
          removeLinksItemById(link.uuid,visibleLinks);
        }
      }
    });
  }
}